package yunjiao.javatutorials.guava.concurrent;

import com.google.common.util.concurrent.Striped;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * 基于条带化的信号量实现，主要用于在需要对不同资源进行细粒度并发控制的场景中
 *
 * @author yangyunjiao
 */
public class StripedSemaphoreExample {
    /**
     * 条带数量选择: 根据预估的并发资源数量设置条带数, 太少会导致竞争，太多会浪费内存
     */

    /**
     * 许可数量设置:
     * permits = 1;  // 严格互斥
     * permits = 5;  // 允许有限并发
     * permits = 10; // 较高并发
     */
    // 创建条带化信号量：50个条带，每个信号量3个许可
    private static final Striped<Semaphore> stripedSemaphores =
            Striped.semaphore(50, 3);

    /**
     * 模拟资源访问 - 使用条带化信号量控制并发
     */
    public static boolean accessResource(String resourceId, long timeout, TimeUnit unit) {
        // 根据resourceId获取对应的信号量
        Semaphore semaphore = stripedSemaphores.get(resourceId);

        try {
            // 尝试在指定时间内获取许可
            if (semaphore.tryAcquire(timeout, unit)) {
                try {
                    // 模拟资源访问操作
                    System.out.println(Thread.currentThread().getName() +
                            " 正在访问资源: " + resourceId +
                            " - 剩余许可: " + semaphore.availablePermits());
                    Thread.sleep(1000); // 模拟业务处理
                    return true;
                } finally {
                    // 释放许可
                    semaphore.release();
                    System.out.println(Thread.currentThread().getName() +
                            " 释放资源: " + resourceId);
                }
            } else {
                System.out.println(Thread.currentThread().getName() +
                        " 获取资源超时: " + resourceId);
                return false;
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        }
    }

    /**
     * 模拟多个线程并发访问不同资源
     */
    public static void main(String[] args) throws InterruptedException {
        // 测试资源ID
        String[] resources = {"user_123", "order_456", "product_789", "user_123", "order_456"};

        // 创建多个线程模拟并发访问
        Thread[] threads = new Thread[10];
        for (int i = 0; i < threads.length; i++) {
            final String resource = resources[i % resources.length];
            final int threadId = i;

            threads[i] = new Thread(() -> {
                boolean success = accessResource(resource, 2, TimeUnit.SECONDS);
                System.out.println("线程 " + threadId + " 访问 " + resource +
                        " 结果: " + (success ? "成功" : "失败"));
            }, "Thread-" + i);
        }

        // 启动所有线程
        for (Thread thread : threads) {
            thread.start();
        }

        // 等待所有线程完成
        for (Thread thread : threads) {
            thread.join();
        }
    }
}
